import fs from 'node:fs'
import { resolve, sep } from 'node:path'
import { defineNitroPlugin } from 'nitropack/dist/runtime/plugin'
import type { Manifest, ManifestChunk } from 'vite'
import parser, { type IBrowser, type IOS } from 'ua-parser-js'
import { useRuntimeConfig } from '#imports'

interface ManifestMeta extends ManifestChunk {
  _legacyFile: string
  isVue?: boolean
  isPolyfill?: boolean
  isLegacy?: boolean
}
const [entry, polyfill, legacy] = ['entry', 'polyfill', 'legacy']
let [polyfillString, matcher] = ['', '']
const manifest: Record<string, ManifestMeta> = {}
const initCache = async (dir: string, serverDir: string, publicDir: string, buildId: string) => {
  const cwd = process.cwd().split(sep)
  if (cwd[cwd.length - 1] === dir) cwd.pop()
  const basePath = cwd.join(sep)
  if (!Object.keys(manifest).length) {
    const file = resolve(basePath, serverDir, './chunks/build/client.manifest.mjs')
    const fest = await (import(file).then(r => r.default || r).then(r => (typeof r === 'function' ? r() : r)) satisfies Promise<Manifest>)
    for (const key in fest) {
      const meta = fest[key] as ManifestMeta
      const { isEntry, isLegacy, isPolyfill } = (manifest['/_nuxt/' + meta.file] = meta)
      const _key = isEntry ? entry : isLegacy ? legacy : isPolyfill ? polyfill : ''
      if (_key) manifest[_key] = meta
    }
  }
  if (!polyfillString && manifest[polyfill]) {
    const file = resolve(basePath, publicDir, `./_nuxt/${manifest[polyfill].file}`)
    polyfillString = fs.readFileSync(file, 'utf8').replace(/^export.*?\{[^{}]*(\{.*?\})+[^{}]*\}/i, '')
  }
  if (buildId) {
    const file = resolve(basePath, publicDir, `./_nuxt/builds/meta/${buildId}.json`)
    matcher = fs.readFileSync(file, 'utf8')
  }
}
const isNeedLegacy = (browser: IBrowser, os: IOS, chrome?: number, safari?: number, edge?: number, firefox?: number) => {
  const { name: n1 = '', version: v1 = '0' } = browser
  const { name: n2 = '', version: v2 = '0' } = os
  if (chrome && n1.includes('Chrome')) return Number.parseFloat(v1) <= chrome
  if (safari && n1.includes('Safari')) return Number.parseFloat(v1) <= safari
  if (safari && n2.includes('iOS')) return Number.parseFloat(v2) <= safari
  if (edge && n1.includes('Edge')) return Number.parseFloat(v1) <= edge
  if (firefox && n1.includes('Firefox')) return Number.parseFloat(v1) <= firefox
  return true
}
const getFilePath = (cacheKey: string) => {
  const { file, _legacyFile } = manifest[cacheKey] || {}
  const src = _legacyFile || file
  return src ? `/_nuxt/${src}` : ''
}
const override = () => {
  // todo 低版本时$fetch获取builds/meta/[buildId].json出错，尚未研究
  return `<script>function override(){if(!self.$fetch){return requestAnimationFrame(override)}var $$fetch=self.$fetch;self.$fetch=function(){if(arguments[0].indexOf('/builds/meta/')>-1)return Promise.resolve(${matcher});return $$fetch.apply(self,arguments)}}override()</script>`
}
export default defineNitroPlugin(async (nitro) => {
  const {
    public: { nuxt3ViteLegacy: { dir, serverDir, publicDir, chrome, safari, edge, firefox } },
    app: { buildId },
  } = useRuntimeConfig() as any
  await initCache(dir, serverDir, publicDir, buildId)

  nitro.hooks.hook('render:html', (html, { event }) => {
    const { browser, os } = parser(event.node.req.headers['user-agent'] || '')
    if (isNeedLegacy(browser, os, chrome, safari, edge, firefox)) {
      const first: string[] = (html.head[0] || '').split(/[\n\r]+/g)
      const urlReg = /^<script.*?type=['"]module['"].*?src=['"](?<src>.*?)['"]|^<link.*?as=['"]script['"].*?href=['"](?<href>.*?)['"]/i
      let [index, src] = [first.length, '']
      for (let i = 0; i < first.length; i++) {
        const { groups } = first[i].match(urlReg) || {}
        if ((src = groups?.src || groups?.href || '') && (index = Math.min(i, index)) > -1) {
          const { _legacyFile, isVue } = manifest[src] || {}
          if (isVue || manifest[src] === manifest.entry || !_legacyFile) {
            first.splice(i--, 1)
          }
          else {
            first[i] = first[i]
              .replace(src, `/_nuxt/${_legacyFile}`)
              .replace(/module(preload)|(\s+crossorigin(\s+|$))/gi, '$1')
          }
        }
      }
      const [legacyFile, entryFile] = [getFilePath(legacy), getFilePath(entry)]
      // 响应头里的LINK比html head里的<link rel=preload>更早预加载
      // response.appendHeader("Link", `<url>;rel=preload;as=script,<url>;rel=preload;as=script`)
      first.splice(index, 0, ...[legacyFile, entryFile].map(href => `<link rel="preload" as="script" href="${href}">`))
      html.head[0] = first.join('\n')
      html.head.push(override())
      html.body.push(`<script src="${legacyFile}"></script><script src="${entryFile}" type="systemjs-module"></script>`)
      if (polyfillString) html.head.unshift(`<script>${polyfillString}</script>`)
    }
  })
})
